package com.angus.modules.content.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.angus.common.core.exception.ServiceException;
import com.angus.common.core.utils.bean.BeanValidators;
import com.angus.common.core.web.domain.AjaxResult;
import com.angus.common.security.utils.SecurityUtils;
import com.angus.modules.content.domain.CNContent;
import com.angus.modules.content.domain.Content;
import com.angus.modules.content.domain.Course;
import com.angus.modules.content.repository.CNContentRepository;
import com.angus.modules.content.repository.CourseRepository;
import com.angus.modules.content.service.ICNContentService;
import com.angus.modules.content.service.ISysUnitService;
import com.angus.modules.content.vo.ContentResultVo;
import com.angus.modules.content.vo.ContentResultVoV2;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.validation.Validator;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author liyanan
 * @date 2024/10/21 5:24 PM
 * @description
 */
@Service
@Slf4j
public class CNContentServiceImpl implements ICNContentService {

    @Resource
    private CNContentRepository cNContentRepository;
    @Resource
    private ISysUnitService sysUnitService;
    @Resource
    private CourseRepository courseRepository;
    @Resource
    protected Validator validator;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String importContent(MultipartFile file, String courseCode) {
        String operatorName = SecurityUtils.getUsername();
        AtomicInteger insertSuccessNum = new AtomicInteger(0);
        AtomicInteger failureNum = new AtomicInteger(0);
        StringBuilder failureMsg = new StringBuilder();
        try {
            List<CNContent> contentList = readExcelFile(file, CNContent.class);
            processContent(contentList, operatorName, courseCode,
                    insertSuccessNum, failureNum, failureMsg);
        }catch (Exception e){
            log.error("文件导入失败：{}", e.getMessage(), e);
            throw new ServiceException("文件导入失败：" + e.getMessage());
        }
        return "数据导入成功！共新增 " + insertSuccessNum + " 条";
    }

    private void processContent(List<CNContent> contentList,
                                String operatorName,
                                String courseCode,
                                AtomicInteger insertSuccessNum,
                                AtomicInteger failureNum,
                                StringBuilder failureMsg) {
        Map<Integer, List<CNContent>> contentMap = contentList.stream().filter(Objects::nonNull)
                .collect(Collectors.groupingBy(s -> Integer.parseInt(s.getGroupFlag().split("-")[0])));
        // 得到 group_flag 用于 sql 查询
        List<String> queList = contentList.stream().filter(Objects::nonNull)
                .map(Content::getGroupFlag).collect(Collectors.toList());
        // 基于 group_flag 查询未删除的数据
        List<CNContent> idempotentList = cNContentRepository.selectWordByGroupFlag(queList,courseCode);
        // 转换成 Map<String, CNContent> 根据 group_flag 用于后续判断是否存在数据库
        Map<String, CNContent> contentIdempotentMap = idempotentList.stream().filter(Objects::nonNull)
                .collect(Collectors.toMap(CNContent::getGroupFlag, s -> s, (existing, replacement) -> existing));
        List<List<Integer>> batches = checkIfUnitIsComplete(contentMap);
        List<CNContent> cnContentList = new ArrayList<>();
        for (List<Integer> batch : batches) {
            for (Integer key : batch) {
                List<CNContent> contents = contentMap.get(key);
                for (CNContent content : contents) {
                    processContentItem(cnContentList,
                            content, operatorName,
                            courseCode, insertSuccessNum,
                            failureNum, failureMsg,
                            contentIdempotentMap);
                }
                if (insertSuccessNum.get() > 0) {
                    sysUnitService.addUnitToMax(key, operatorName,courseCode);
                }
            }
        }
        if (CollUtil.isNotEmpty(cnContentList)) {
            cNContentRepository.saveBatch(cnContentList);
            cNContentRepository.updateSortToId();
        }
    }
    private void processContentItem(List<CNContent> cnContentList,
                                    CNContent content, String operatorName,
                                    String courseCode, AtomicInteger insertSuccessNum,
                                    AtomicInteger failureNum, StringBuilder failureMsg,
                                    Map<String, CNContent> contentIdempotentMap) {
        CNContent existingContent = contentIdempotentMap.get(content.getGroupFlag());
        if (existingContent == null) {
            BeanValidators.validateWithException(validator, content);
            content.setCreateBy(operatorName);
            content.setCourseCode(courseCode);
            content.setGroupId(Long.parseLong(content.getGroupFlag().split("-")[0]));
            insertSuccessNum.incrementAndGet();
            cnContentList.add(content);
        } else {
            failureNum.incrementAndGet();
            failureMsg.append("//n").append(content.getCnWord()).append(" 已存在");
        }
    }

    @Override
    public List<CNContent> getContentList(String courseCode,Integer unitNum) {
        return cNContentRepository.selectContentList(courseCode,unitNum);
    }

    @Override
    public AjaxResult addContent(CNContent content) {
        String operatorName = SecurityUtils.getUsername();
        // 查询现有记录
        List<CNContent> contentData = cNContentRepository.selectContentByGroupID(
                content.getCourseCode(),
                content.getGroupId()
        );
        // 检查 cnWord 是否已存在
        Map<String, CNContent> contentMap = contentData.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toMap(CNContent::getCnWord, s->s, (existing, replacement) -> existing));

        if (contentMap.containsKey(content.getCnWord())) {
            return AjaxResult.warn("不支持同一个集束才字词重复");
        }
        // 检查组内容量
        if (contentData.size() >= GROUP_COUNT) {
            return AjaxResult.warn(String.format("超出最大集束插入数量，现判断数量策略为 %s", GROUP_COUNT));
        }
        // 计算 nextGroupFlag
        int nextGroupFlag = 1; // 默认值
        if (!contentData.isEmpty()) {
            // 按 groupFlag 排序，取最大值
            Optional<CNContent> maxContentOpt = Optional.ofNullable(contentData.stream()
                    .filter(c -> c.getId() != null) // 过滤掉 id 为 null 的记录
                    .max(Comparator.comparingLong(CNContent::getId))
                    .orElse(null));

            log.info(String.format("maxContentOpt: {}", maxContentOpt));
            if (maxContentOpt.isPresent()) {
                try {
                    String[] parts = maxContentOpt.get().getGroupFlag().split("-");
                    nextGroupFlag = Integer.parseInt(parts[1]) + 1;
                } catch (Exception e) {
                    nextGroupFlag = 1; // 异常时使用默认值
                }
            }
        }
        // 设置 groupFlag 并保存
        content.setCreateBy(operatorName);
        content.setGroupFlag(content.getGroupId() + "-" + nextGroupFlag);
        if (!cNContentRepository.save(content)){
            return AjaxResult.warn("添加字词失败");
        }
        if (content.getSort() > 0){
            cNContentRepository.updateSortToId();
        }
        return AjaxResult.success("添加字词成功");
    }

    @Override
    public Boolean updateContent(CNContent content) {
        return cNContentRepository.updateById(content);
    }

    @Override
    public Integer deleteByIds(List<Long> ids, Integer unitNum,String courseCode) {

        Long count = cNContentRepository.selectNotInIds(ids);
        if(count<=0){
            sysUnitService.removeUnit(courseCode,unitNum);
        }
        return cNContentRepository.getBaseMapper().deleteBatchIds(ids);
    }

    @Override
    public ContentResultVo getContentByGroupId(String courseCode, Integer unitNum, Long groupId) {
        Course course = courseRepository.getCourseByCode(courseCode);
        List<CNContent> cnContentList = cNContentRepository.selectContentByGroupId(courseCode, unitNum, groupId);
        ContentResultVo resultVo = new ContentResultVo();
        List<CNContent> nextEnContentList = cNContentRepository.selectContentByGroupId(courseCode, unitNum, groupId+1);
        if(CollUtil.isNotEmpty(nextEnContentList)){
            nextEnContentList.stream()
                    .filter(s -> s.getGroupFlag().contains("s"))
                    .findFirst()
                    .ifPresent(s -> resultVo.setNextGroupId(nextEnContentList.get(0).getGroupId()));
        }else {
            resultVo.setNextGroupId(groupId);
        }
        if(CollUtil.isNotEmpty(cnContentList)){
            List<CNContent> filteredContentList = new ArrayList<>();
            for (CNContent content : cnContentList) {
                content.setCourseName(course.getCourseName());
                if(content.getGroupFlag().contains("s")){
                    resultVo.setSentenceStr(content.getCnWord().substring(0, content.getCnWord().length()-2));
                } else {
                    filteredContentList.add(content);
                }
            }
            resultVo.setContentList(filteredContentList);
            resultVo.setCourseCode(courseCode);
            resultVo.setGroupId(groupId);
        }
        return resultVo;
    }

    @Override
    public ContentResultVoV2 getContentByGroupFlag(String courseCode, Integer unitNum, String groupFlag) {
        CNContent cnContent = cNContentRepository.selectContentByGroupFlag(courseCode, unitNum, groupFlag);
        ContentResultVoV2 resultVo = new ContentResultVoV2();
        resultVo.setInfo(cnContent);
        return resultVo;
    }

    @Override
    public AjaxResult syncSortID(){
        if (cNContentRepository.updateSortToId()){
            return  AjaxResult.success("同步成功");
        }
        return  AjaxResult.warn("同步失败");
    }
}
